package com.twq.types.typeximplicit

import scala.reflect.ClassTag

/**
 * Created by tangweiqun on 2017/11/20.
 */
object TypeTagsTest {
  def main(args: Array[String]): Unit = {
    //1、TypeTags主要包含以下两种：
    //1.1 scala.reflect.api.TypeTags#TypeTag
    // 封装了一个scala类型的所有类型描述信息，比如TypeTag[List[String]]，则包含了scala.List[String]的信息

    //1.2 scala.reflect.ClassTag
    // 封装了一个scala类型的部分类型描述信息，比如ClassTag[List[String]]，则包含了scala.collection.immutable.List的信息

    //2、TypeTag总是编译器来创建的，获取TypeTag有三种方式：

    import scala.reflect.runtime.universe._

    //2.1 通过类型 `TypeTag[T]`, `ClassTag[T]`的隐式参数来创建
    def paramInfo[T](x: T)(implicit tag: TypeTag[T]): Unit = {
      val targs = tag.tpe match {
        case TypeRef(_, _, args) => args
      }
      println(s"type of $x has type arguments $targs")
    }

    paramInfo(42)
    paramInfo(List(1, 2))

    //2.2 通过一个类型参数的上下文界定
    def paramInfo_2[T: TypeTag](x: T): Unit = {
      val targs = typeOf[T] match {
        case TypeRef(_, _, args) => args
      }
      println(s"type of $x has type arguments $targs")
    }

    paramInfo_2(42)
    paramInfo_2(List(1, 2))

    //2.3 通过方法来创建
    val tt = typeTag[List[String]]
    val tt2 = implicitly[TypeTag[List[String]]]

    import scala.reflect._
    val ct = classTag[List[String]]
    val ct2 = implicitly[ClassTag[List[String]]]


    //4、TypeTags and Manifests(2.8引入，2.10 deprecate)
    //TypeTag和Manifest对应
    //ClassTag和ClassManifest对应
    //TypeTags是Manifests的丰富，并且TypeTags完全与scala的反射机制集成

  }

  //scala中的数组是Array[Int]表示的，但是在JVM中，每一个类型的数组都是单组的类型，比如int[],double[], Object[]
  //所以我们需要捕获编译时的类型信息，然后用于运行时计算
  //def first[A](x: Array[A]) = Array(x(0))

  //我们可以用ClassTag来捕获编译时的类型
  def first[A: ClassTag](x: Array[A]) = Array(x(0))
}
